//
// Created by lz on 3/1/21.
//

#include "net/poller/PollPoller.h"

#include "base/Logging.h"
#include "base/Types.h"
#include "net/Channel.h"

#include <cassert>
#include <cerrno>
#include <poll.h>

using namespace lzweb;
using namespace lzweb::net;

PollPoller::PollPoller(EventLoop* loop)
: Poller(loop)
{
}
PollPoller::~PollPoller() = default;

TimeStamp PollPoller::poll(int timeoutMs, Poller::ChannelList* activeChannels)
{
	// XXX pollfds_ shouldn't change
	int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);
	int savedErrno = errno;

	TimeStamp now(TimeStamp::now());

	if(numEvents > 0)
	{
		LOG_TRACE << numEvents << " events happened";
		fillActiveChannels(numEvents, activeChannels);
	}
	else if (numEvents == 0)
	{
		LOG_TRACE << " nothing happened";
	}
	else
	{
		// EINTR -> 系统中断
		if(savedErrno !=EINTR)
		{
			errno = savedErrno;
			LOG_SYSERR << "PollPoller::poll()";
		}
	}

	return now;
}


// private 成员函数 ， 供内部使用，将这次poll的触发文件描述符及其事件的channel加在activeChannels中
void PollPoller::fillActiveChannels(int numEvents, ChannelList* activeChannels) const
{
	for (PollFdList::const_iterator pfd = pollfds_.begin();
		 pfd != pollfds_.end() && numEvents > 0; ++pfd)
	{
		if (pfd->revents > 0)
		{
			--numEvents;
			ChannelMap::const_iterator ch = channels_.find(pfd->fd);
			assert(ch != channels_.end());

			Channel* channel = ch->second;
			assert(channel->fd() == pfd->fd);

			channel->set_revents(pfd->revents);

			//pfd->revents = 0;
			activeChannels->push_back(channel);

		}
	}
}

void PollPoller::updateChannel(Channel* channel)
{
	Poller::assertInLoopThread();
	LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();

	//channel->index() < 0 说明不在channels_
	if (channel->index() < 0)
	{
		// a new one, add to pollfds_
		assert(channels_.find(channel->fd()) == channels_.end());
		struct pollfd pfd;
		pfd.fd = channel->fd();
		pfd.events = static_cast<short>(channel->events());
		pfd.revents = 0;
		pollfds_.push_back(pfd);
		int idx = static_cast<int>(pollfds_.size()) -1;
		channel->set_index(idx);
		channels_[pfd.fd] = channel;
	}
	else
	{
		//update existing one
		assert(channels_.find(channel->fd()) != channels_.end());
		assert(channels_[channel->fd()] == channel);
		int idx = channel->index();
		assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));

		struct pollfd& pfd = pollfds_[idx];
		assert(pfd.fd == channel->fd() || pfd.fd == -channel->fd()-1); // 下面解释为何有负数

		pfd.fd = channel->fd();
		pfd.events = static_cast<short>(channel->events());
		pfd.revents = 0;
		//通过将文件描述符设为负数，让poll来忽略该fd
		if(channel->isNoneEvent())
		{
			pfd.fd = -channel->fd() -1;
		}
	}
}

void PollPoller::removeChannel(Channel* channel)
{
	//指明调用的assertInLoopThread
	Poller::assertInLoopThread();

	LOG_TRACE << "fd = " << channel->fd();
	assert(channels_.find(channel->fd()) != channels_.end());
	assert(channels_[channel->fd()] == channel);
	//移除前保证没有关注的事件
	assert(channel->isNoneEvent());
	int idx = channel->index();

	assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));

	const struct pollfd& pfd = pollfds_[idx];
	(void) pfd;
	assert(pfd.fd == -channel->fd()-1 && pfd.events == channel->events());

	//在channelMap中删除
	size_t n = channels_.erase(channel->fd());
	assert(n == 1);(void) n;
	//在pollfds_中删除
	if(implicit_cast<size_t>(idx) == pollfds_.size() -1)
	{
		pollfds_.pop_back();
	}
	else
	{
		int channelAtEnd = pollfds_.back().fd;
		//交换最后一个元素
		iter_swap(pollfds_.begin() + idx, pollfds_.end() - 1);
		if (channelAtEnd < 0)
		{
			channelAtEnd = -channelAtEnd -1;
		}
		channels_[channelAtEnd]->set_index(idx);
		pollfds_.pop_back();
	}
}



